package com.mytijian.task.repository.database.repository;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.mytijian.task.enums.TaskStatusEnum;
import com.mytijian.task.model.SubTask;
import com.mytijian.task.model.Task;
import com.mytijian.task.repository.database.dao.TaskDAO;
import com.mytijian.task.repository.database.dao.daoobject.SubTaskCount;
import com.mytijian.task.repository.database.dao.daoobject.SubTaskDO;
import com.mytijian.task.repository.database.dao.daoobject.TaskDO;
import com.mytijian.task.repository.database.dao.daoobject.TaskIdentifyDO;
import com.mytijian.task.repository.database.utils.ModelUtils;
import com.mytijian.task.repository.model.GroupExecutorDTO;
import com.mytijian.task.model.TaskValue;
import com.mytijian.task.enums.SubTaskStatusEnum;
import com.mytijian.task.repository.TaskRepository;
import com.mytijian.task.repository.model.TaskIdentifyDTO;
import com.mytijian.task.model.TaskIdentify;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class MysqlTaskRepository implements TaskRepository {
    private Logger logger = LoggerFactory.getLogger(MysqlTaskRepository.class);
    private TaskDAO taskDAO;
    private static SqlSessionTemplate sqlSessionTemplate;
    static {
        String resource = "mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 构建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
    }

    private void insert(String sql, Object o){
            sqlSessionTemplate.insert(sql, o);
    }
    @Override
    public int insert(Task task) {
        TaskDO taskDO = ModelUtils.toTaskDO(task);
//        taskDAO.insert(taskDO);
        insert("insert", taskDO);
        task.setId(taskDO.getId());
        return taskDO.getId();
    }

    @Override
    public void batchInsert(List<SubTask> subTasks) {
        List<SubTaskDO> subTaskDOS = ModelUtils.toSubTaskDOs(subTasks);
        //taskDAO.batchInsert(subTaskDOS);
        insert("batchInsert", subTaskDOS);
        for (int i = 0; i < subTaskDOS.size(); i++) {
            subTasks.get(i).setId(subTaskDOS.get(i).getId());
        }
    }

    public <T> List<T> getList(String sql,Object param) {
        List<T> list = sqlSessionTemplate.selectList(sql, param);
        return list;
    }

    @Override
    public List<Task> getTasksByIds(List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Lists.newArrayList();
        }
//        List<TaskDO> taskDOS = taskDAO.getTasksByIds(ids);
        List<TaskDO> taskDOS = getList("getTasksByIds", ids);
//        List<SubTaskCount> subTaskCounts = taskDAO.countSubTaskGroupByTaskIdStatus(ids);
        Map<String, Object> stringObjectMap = new HashMap<>();
        stringObjectMap.put("taskIds", ids);
        List<SubTaskCount> subTaskCounts = getList("countSubTaskGroupByTaskIdStatus", stringObjectMap);
        Map<String, SubTaskCount> map = subTaskCounts.stream().collect(Collectors.toMap(subTaskCount -> {
            return getKey(subTaskCount.getTaskId(), subTaskCount.getStatus());
        }, Function.identity()));
        List<Task> tasks =  ModelUtils.toTasks(taskDOS);
        for (Task task : tasks) {
            SubTaskCount failSubTaskCount = map.get(getKey(task.getId(), SubTaskStatusEnum.process_fail.getCode()));
            task.setFailNum(0);
            task.setSuccessNum(0);
            if (failSubTaskCount != null) {
                task.setFailNum(failSubTaskCount.getCount());
            }
            SubTaskCount successSubTaskCount = map.get(getKey(task.getId(), SubTaskStatusEnum.process_success.getCode()));
            if (successSubTaskCount != null) {
                task.setSuccessNum(successSubTaskCount.getCount());
            }
        }
        return tasks;
    }


    private String getKey(Integer taskId, Integer subTaskStatus) {
        return taskId + ":" + subTaskStatus;
    }
    @Override
    public List<SubTask> getSubTaskInId(Integer taskId, Integer beginId, Integer endId) {
        //List<SubTaskDO> subTaskDOS = taskDAO.getSubTasksInId(taskId,beginId, endId);
        Map<String, Object> map = new HashMap<>();
        map.put("taskId", taskId);
        map.put("beginId", beginId);
        map.put("endId", endId);
        List<SubTaskDO> subTaskDOS = getList("getSubTasksInId", map);
        return ModelUtils.toSubTasks(subTaskDOS);
    }

    @Override
    public List<SubTask> getSubTaskByStatus(Integer taskId, List<Integer> inStatus) {
//        List<SubTaskDO> subTasks = taskDAO.getSubTaskByTaskIds(Lists.newArrayList(taskId), inStatus);
        Map<String, Object> map = new HashMap<>();
        map.put("list", Lists.newArrayList(taskId));
        map.put("status", inStatus);
        List<SubTaskDO> subTasks = getList("getSubTaskByTaskIds", map);
        return ModelUtils.toSubTasks(subTasks);

    }
    public void update(String sql, Object o) {
        sqlSessionTemplate.update(sql, o);
    }
    @Override
    public void processFail(Integer subTaskId, Integer failScene, String failText) {
        //taskDAO.batchUpdateSubTask(null, Lists.newArrayList(id), status, failScene, failText);
        Map<String, Object> map = new HashMap<>();
        map.put("subTaskIds", Lists.newArrayList(subTaskId));
        map.put("status", SubTaskStatusEnum.process_fail.getCode());
        map.put("failScene", failScene);
        map.put("failText", failText);
        update("batchUpdateSubTask", map);
    }

    @Override
    public void processSuccess(Integer taskId, List<Integer> subTaskIds) {
        Map<String, Object> map = new HashMap<>();
        map.put("taskId", taskId);
        map.put("status", SubTaskStatusEnum.process_success.getCode());
        map.put("failScene", null);
        map.put("failText", null);
        map.put("subTaskIds", subTaskIds);
        //taskDAO.batchUpdateSubTask(taskId, subTaskIds, status, failScene, null);
        update("batchUpdateSubTask", map);
    }

    @Override
    public List<Integer> getSubTaskIds(Integer taskId, Integer beginSubTaskId, Integer endSubTaskId, List<Integer> inStatus) {
        Map<String, Object> map = new HashMap<>();
        map.put("beginSubTaskId", beginSubTaskId);
        map.put("endSubTaskId", endSubTaskId);
        map.put("status", inStatus);
        map.put("taskId", taskId);
        return getList("getSubTaskIds", map);
    }
    private <T> T selectOne(String sql, Object o) {
        try {
           return sqlSessionTemplate.selectOne(sql, o);
        }catch (Exception e) {
            throw e;
        }finally {
        }
    }

    @Override
    public void save(TaskIdentify taskIdentify, TaskValue taskValue, String ip) {
        //TaskIdentifyDO taskIdentifyDO = taskDAO.getTaskIdentify(taskIdentify);
        TaskIdentifyDO taskIdentifyDO = selectOne("getTaskIdentify", taskIdentify);
        if (taskIdentifyDO == null) {
            //taskDAO.saveTaskIdentify(buildTaskIdentifyDO(taskIdentify, taskValue, ip));
            insert("saveTaskIdentify", buildTaskIdentifyDO(taskIdentify, taskValue, ip));
            return;
        }
        List<String> ips = JSON.parseArray(taskIdentifyDO.getIps(), String.class);
        // 最后一个表示最新的ip
        if (!ips.get(ips.size() -1).equals(ip)) {
            ips.add(ip);
        }
        taskIdentifyDO.setIps(JSON.toJSONString(ips));
        taskIdentifyDO.setTaskValue(taskValue.getValue());
        taskIdentifyDO.setTaskStatus(taskValue.getTaskStuts());
        //taskDAO.updateTaskIdentify(taskIdentifyDO);
        update("updateTaskIdentify", taskIdentifyDO);
    }

    @Override
    public TaskStatusEnum completeStatus(List<TaskIdentifyDTO> taskIdentifies) {
        TaskIdentifyDTO taskIdentifyDTO = taskIdentifies.get(0);
        Integer taskId = taskIdentifyDTO.getTaskId();
        //TaskDO task = taskDAO.getTasksByIds(Lists.newArrayList(taskId)).get(0);
        TaskDO task = (TaskDO) getList("getTasksByIds", Lists.newArrayList(taskId)).get(0);
        if (task.getStatus() == TaskStatusEnum.complete.getCode() || task.getStatus() == TaskStatusEnum.stop.getCode() ) {
            logger.info("task 处于终态 无需执行 taskid = {}", taskId);
            return TaskStatusEnum.getByCode(task.getStatus());
        }
        TaskStatusEnum taskStatusEnum = getCompleteStatus(taskIdentifies);
        if (taskStatusEnum != null) {
            //taskDAO.updateTaskStatus(taskId, taskStatusEnum.getCode());
            Map<String, Object> map = new HashMap<>();
            map.put("taskId", taskId);
            map.put("status", taskStatusEnum.getCode());
            update("updateTaskStatus",map);
            return taskStatusEnum;
        }
        return null;
    }
    private TaskStatusEnum getCompleteStatus(List<TaskIdentifyDTO> taskIdentifyDOS) {
        for (TaskIdentifyDTO taskIdentifyDO : taskIdentifyDOS) {
            if (taskIdentifyDO.getTaskStatus() != TaskStatusEnum.complete.getCode()) {
                return null;
            }
        }
        return TaskStatusEnum.complete;
    }
    @Override
    public List<TaskIdentifyDTO> getTaskIdentifies(Integer taskId) {
        //List<TaskIdentifyDO> taskIdentifyDOS = taskDAO.getTaskIdentifies(taskId);
        Map<String, Object> map = new HashMap<>();
        map.put("taskId", taskId);
        List<TaskIdentifyDO> taskIdentifyDOS = getList("getTaskIdentifies", map);
        List<TaskIdentifyDTO> taskIdentifyDTOS = new ArrayList<>(taskIdentifyDOS.size());
        for (TaskIdentifyDO taskIdentifyDO : taskIdentifyDOS) {
            TaskIdentifyDTO taskIdentifyDTO = new TaskIdentifyDTO();
            BeanUtils.copyProperties(taskIdentifyDO, taskIdentifyDTO);
            List<String> ips = JSON.parseArray(taskIdentifyDO.getIps(), String.class);
            taskIdentifyDTO.setIps(ips);
            taskIdentifyDTO.setCurrentIp(ips.get(ips.size() - 1));
            taskIdentifyDTO.setTaskValue(TaskValue.build(taskIdentifyDO.getTaskValue()));
            taskIdentifyDTOS.add(taskIdentifyDTO);
        }
        return taskIdentifyDTOS;
    }

    @Override
    public void updateTaskStatus(Integer taskId, Integer taskStatus) {
        //taskDAO.updateTaskStatus(taskId, taskStatus);
        Map<String, Object> map = new HashMap<>();
        map.put("taskId", taskId);
        map.put("status", taskStatus);
        update("updateTaskStatus", map);
    }

    @Override
    public List<GroupExecutorDTO> getGroupExecutorsAll() {
        return getList("getGroupExecutorsAll", null);
    }
    @Override
    public void saveGroupExecutors(GroupExecutorDTO executorDTO) {
        Map<String, Object> map = new HashMap<>();
        map.put("groupKey", executorDTO.getGroupKey());
        GroupExecutorDTO queryFromDB = selectOne("getGroupExecutorByGroupKey", map);
        if (queryFromDB == null) {
            insert("insertGroupExecutor", executorDTO);
        }else {
            update("updateGroupExecutor", executorDTO);
        }


    }
    public static void main(String[] args) {
        MysqlTaskRepository mysqlTaskRepository = new MysqlTaskRepository();
        Map<String, Object> map = new HashMap<>();

        map.put("taskId", 23);
        map.put("status", TaskStatusEnum.processing.getCode());
        mysqlTaskRepository.update("updateTaskStatus", map);
    }
    private TaskIdentifyDO buildTaskIdentifyDO(TaskIdentify taskIdentify, TaskValue taskValue, String ip) {
        TaskIdentifyDO taskIdentifyDO = new TaskIdentifyDO();
        BeanUtils.copyProperties(taskIdentify, taskIdentifyDO);
        taskIdentifyDO.setTaskStatus(taskValue.getTaskStuts());
        taskIdentifyDO.setTaskValue(taskValue.getValue());
        taskIdentifyDO.setIps(JSON.toJSONString(Lists.newArrayList(ip)));
        return taskIdentifyDO;
    }
}
